gusucode.com > VC++ RingSDK界面库 > VC++ RingSDK界面库/code/libsrc/ringdows/ringheader.cpp

    /**********************************************************************
//
//
//        ##########                          ######  #########   # ######    #
//      #############                      ########### ######### #########  ###
//     ######## # ###  ##                 ############# ##    ## #####  # ####
//     ####  ##    ## ###                 ###     ### # #      #####   #####
//          #     ###  #            #     ##       ##  ##      ##     ###
//         ## ###### ##      ##  ####    ####          #       #     ##
//       #########  ###  ## ### #######   ######      ##      ##    ###
//      ######      ##  ######  ##  ##       ####     #      ##     ####
//     #######     ##   ###### ##  ###          ##   ##     ###    ######
//     #########   ##  ###### ## ######         ### ##    ###      #  #####
//    ##    ###### ####### ### #### ##  ## #######  ########      ##    ####
//    ##      #### ###  #  ### ### ##  ##########   ######       ##      ####
//   ##         ##                ##   #########    ####         #         ##
//               #              ###
//                              ##
//                             ###
//                             ##
//
//
//							临风程序界面类库 ringdows.lib
//作者:临风
//
//版本:1.0
//
//声明:本类库可以自由使用而不须对作者作出任何回报,但作者希望能得到
//		  你的鼓励和支持。你可以对类库源码作出修改和改进,但希望你能在
//		  修改的同时给作者一份同样的副本。
//		  本类库不得用于任何商业用途,如确实需要,请与作者联系。
//
//e-mail:ringphone@sina.com
//
//原文件名:ringheader.cpp
//
//说明:header控件控制
//
//功能扩展:作为ListView的Header允许隐藏/显示列,为避免ListView删除和插入
//				列需要重新更新每一项的代价,采用设置列宽为0的方式,但是用户
//				拖动列头会显示出隐藏的列,因此在窗口过程做了处理,除第一列外
//				(第一列不可删除)其他列头均设置一个LONG[2]数组,LONG[0]为
//				用户设置的数据,LONG[1]为[隐藏标志1:隐藏,0:显示(最高位)]
//				[原序号(ORDER序号,3BIT)][原宽度(低4位)],因为用户拖动列
//				时并不发送HDM_HITTEST消息来检测鼠标位于什么位置,所以不能通过
//				处理HDM_HITTEST消息改变返回值来达到目的。只能处理为隐藏列全部
//				移动到最前面,这样鼠标只有在最左边时才会变为拉开状态,此时处理
//				WM_LBUTTONDOWN和WM_LBUTTONDBCLK消息,通过HDM_HITTEST消息检测
//				如果是HHT_ONDIVOPEN且该列隐藏则直接返回,这样就达到了目的。
//
**********************************************************************/
#define MAKE_SELF_LIB
#include "ringdows.h"
#include <malloc.h>

RingHeader::RingHeader()
{
	m_hImage = NULL;
	m_CoolMenu = NULL;
	m_bCanHideColumn = FALSE;
	m_nHideColCnt = 0;
}

RingHeader::~RingHeader()
{
	if(m_hImage)
		ImageList_Destroy(m_hImage);
	delete m_CoolMenu;
}

void RingHeader::InitImageList(int cx,int cy)
{
	if(m_hImage == NULL)
	{
		m_hImage = ImageList_Create(cx,cy,ILC_COLOR32|ILC_MASK,5,5);
		Header_SetImageList(m_hWnd,m_hImage);
	}
}

////////////////////////////////////////////////////
//
//添加图标,必须已设置过IMAGELIST或调用过InitImageList(),
//
//返回:图标序号
//
////////////////////////////////////////////////////
int RingHeader::AddIcon(LPCTSTR lpszIcon,HINSTANCE hInst)
{
	//因为有可能调用过SetImageList,因此不能使用内置的m_hImage,
	//必须Header_GetImageList
	HIMAGELIST himl = Header_GetImageList(m_hWnd);
	HICON hicon;
	int x,y;
	int nIndex = -1;
	
	if(himl == NULL)
		InitImageList();

	if(himl)
	{
		if(!ImageList_GetIconSize(himl,&x,&y))
			x = y = 16;
		
		hicon = (HICON)LoadImage(hInst,lpszIcon,IMAGE_ICON,x,y,LR_DEFAULTCOLOR);
		if(hicon)
			nIndex = ImageList_AddIcon(himl,hicon);
	}
	
	return nIndex;
}

int RingHeader::AddIcon(UINT uidIcon,HINSTANCE hInst)
{
	return AddIcon(MAKEINTRESOURCE(uidIcon),hInst);
}

int RingHeader::AddIcon(HICON hicon)
{
	if(hicon == NULL)
		return -1;
	
	int nIndex = -1;
	HIMAGELIST himl = Header_GetImageList(m_hWnd);
	if(himl == NULL)
		InitImageList();

	if(himl)
		nIndex = ImageList_AddIcon(himl,hicon);
	
	return nIndex;
}

BOOL RingHeader::SetItemWidth(int iCol,int nWidth)
{
	HDITEM hdi;
	hdi.mask = HDI_WIDTH;
	hdi.cxy = nWidth;
	return Header_SetItem(m_hWnd,iCol,&hdi);
}

LONG RingHeader::GetData(int index)
{
	HDITEM hdi;

	hdi.mask = HDI_LPARAM;
	hdi.lParam = 0;

	Header_GetItem(m_hWnd,index,&hdi);
	return hdi.lParam;
}

BOOL RingHeader::SetData(int index,LONG lParam)
{
	HDITEM hdi;
	
	hdi.mask = HDI_LPARAM;
	hdi.lParam = lParam;
	
	return Header_SetItem(m_hWnd,index,&hdi);
}

LPLONG RingHeader::GetRealData(int index)
{
	HDITEM hdi;
	
	hdi.mask = HDI_LPARAM;
	hdi.lParam = 0;
	
	CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,index,(LPARAM)&hdi);
	return (LPLONG)hdi.lParam;
}

BOOL RingHeader::SetRealData(int index,LONG lParam)
{
	HDITEM hdi;
	
	hdi.mask = HDI_LPARAM;
	hdi.lParam = lParam;
	
	return CallWindowProc(m_DefProc,m_hWnd,HDM_SETITEM,index,(LPARAM)&hdi);
}

LPLONG RingHeader::GetHDInfo(int index)
{
	LPLONG lpl = GetRealData(index);
	if(lpl)
		return lpl + 1;
	else
		return NULL;
}

//子项操作
int RingHeader::AddItem(int index,LPCTSTR szText,LPARAM lParam,int idIcon)
{
	HDITEM hdi;
	
	hdi.mask = 0;
	
	if(idIcon >= 0)
	{
		hdi.mask = HDI_IMAGE;
		hdi.iImage = idIcon;
	}
	
	if(szText)
	{
		hdi.mask |= HDI_TEXT;
		hdi.pszText = (LPSTR)szText;
		hdi.cchTextMax = strlen(szText);
	}	
	return Header_InsertItem(m_hWnd,index,&hdi);
}

int RingHeader::AddItem(int index,int nVal,LPARAM lParam,int idIcon)
{
	char s[16];
	wsprintf(s,"%d\0",nVal);
	return AddItem(index,s,lParam,idIcon);
}

void RingHeader::Select(int index,int idIcon)
{
	HDITEM hdi;
	
	hdi.mask = HDI_IMAGE;
	hdi.iImage = idIcon;
	
	Header_SetItem(m_hWnd,index,&hdi);
}

//虚函数重载
BOOL RingHeader::SetValue(int val,int index)
{
	char szText[16];
	wsprintf(szText,"%d\0",val);
	return SetValue(szText,index);
}

BOOL RingHeader::SetValue(LPCTSTR lpszVal,int index)
{
	HDITEM hdi;
	
	hdi.mask = 0;
	
	if(lpszVal)
	{
		hdi.mask = HDI_TEXT;
		hdi.pszText = (LPSTR)lpszVal;
		hdi.cchTextMax = strlen(lpszVal);
	}	
	return Header_SetItem(m_hWnd,index,&hdi);
}

int RingHeader::GetOrder(int index)
{
	HDITEM hdi;
	
	hdi.mask = HDI_ORDER;
	
	if(Header_GetItem(m_hWnd,index,&hdi))
		return hdi.iOrder;
	else
		return -1;
}

int RingHeader::GetItemText(int index,LPTSTR lpBuf,int size)
{
	HDITEM hdi;
	
	hdi.mask = HDI_TEXT;
	hdi.pszText = (LPSTR)lpBuf;
	hdi.cchTextMax = size;

	if(Header_GetItem(m_hWnd,index,&hdi))
		return lstrlen(lpBuf);
	else
		return 0;
}

BOOL RingHeader::IsColumnHide(int iCol)
{
	LPLONG lpl = GetRealData(iCol);
	if(lpl)
		return HDCOL_ISHIDDEN(lpl[1]);
	else
		return FALSE;
}

//获取列头宽度,nMode:如果列头未隐藏,返回实际宽度,
//如果列头已隐藏且nMode=SW_HIDE,返回未隐藏前的宽度
int RingHeader::GetItemWidth(int iCol,int nMode/*=SW_SHOW*/)
{
	HDITEM hd;
	hd.mask = HDI_WIDTH|HDI_LPARAM;

	if(CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,iCol,(LPARAM)&hd))
	{
		if(m_bCanHideColumn && nMode == SW_HIDE)
		{
			LPLONG lpl = (LPLONG)hd.lParam;
			if(lpl)
				return LOWORD(lpl[1]);
		}
		else
			return hd.cxy;
	}
	return -1;
}

///////////////////////////////////////////////////////////
//
//本窗口过程主要对绑定数据进行处理,由于允许显示/隐藏列头,
//因此每个列头的DATA都是一个LONG[2]数组,第一个为用户设置
//的数据,第二个为记录列头宽度和显示与否标记
//
//第一列不可隐藏
//
///////////////////////////////////////////////////////////
LRESULT RingHeader::RingdowProc(HWND hWnd,RINGPARAMS param)
{
	LRESULT res;
	switch(param.uMsg)
	{
		case HDM_DELETEITEM:
			if(m_bCanHideColumn && param.wParam != 0)
			{
				//销毁列头宽度和显示与否标记,恢复用户数据
				LPLONG lpl = GetRealData(param.wParam);
				if(lpl)
				{
					LONG lUserData = *lpl;
					Del(lpl);
					SetRealData(param.wParam,lUserData);
				}
			}
			break;
		case HDM_INSERTITEM:
			res = CallWindowProc(m_DefProc,m_hWnd,param.uMsg,param.wParam,param.lParam);
			if(res != -1 && m_bCanHideColumn)
			{
				//初始化用户数据
				LPLONG lpl = (LPLONG)New(sizeof(LONG)*2);
				if(lpl)
				{
					LPHDITEM phdi = (LPHDITEM)param.lParam;
					if(MASK_MATCH(phdi->mask,HDI_LPARAM))
						*lpl = phdi->lParam;
					phdi->mask = HDI_LPARAM;
					phdi->lParam = (LONG)lpl;
					lpl[1] = MAKELONG(GetItemWidth(res),res);
					CallWindowProc(m_DefProc,m_hWnd,HDM_SETITEM,res,(LPARAM)phdi);
				}
			}
			return res;
		case HDM_GETITEM:
			res = CallWindowProc(m_DefProc,m_hWnd,param.uMsg,param.wParam,param.lParam);
			if(m_bCanHideColumn && param.wParam != 0)
			{
				//设置用户数据返回
				LPHDITEM lphd = (LPHDITEM)param.lParam;
				if(res && lphd && MASK_MATCH(lphd->mask,HDI_LPARAM) && lphd->lParam)
					lphd->lParam = *((LPLONG)lphd->lParam);
			}
			return res;			
		case HDM_SETITEM:
			if(m_bCanHideColumn && param.wParam != 0)
			{
				LPHDITEM lphd = (LPHDITEM)param.lParam;
				if(lphd && (lphd->mask & (HDI_LPARAM|HDI_WIDTH)))
				{
					//设置数据,需要获取原数据数组
					BOOL bHide = FALSE;
					HDITEM hd;
					hd.mask = HDI_LPARAM|HDI_WIDTH;
					hd.lParam = 0;
					if(CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,param.wParam,(LONG)&hd))
					{
						if(hd.lParam)
						{
							LPLONG lpl = (LPLONG)hd.lParam;
							if(MASK_MATCH(lphd->mask,HDI_LPARAM))
								*lpl = lphd->lParam;
							//设置宽度,记录
							if(MASK_MATCH(lphd->mask,HDI_WIDTH))
							{
								bHide = HDCOL_ISHIDDEN(lpl[1]);
								lpl[1] &= 0xFFFF0000;
								lpl[1] |= lphd->cxy;
							}
						}						
					}
					memcpy(&hd,lphd,sizeof(HDITEM));
					//数据已设置,取消该标志
					hd.mask &= ~HDI_LPARAM;
					if(bHide && hd.cxy != 0)	//该列隐藏且设置宽度不为0,仅在数据中设置宽度,不实际设置
						hd.mask &= ~HDI_WIDTH;					
					return CallWindowProc(m_DefProc,m_hWnd,param.uMsg,param.wParam,(LPARAM)&hd);
				}
			}
			break;
		case HDM_ENABLECOLUMNHIDE:
			//自定义消息,允许/禁止隐藏列头,wParam为允许/禁止标志
			if((m_bCanHideColumn && !param.wParam) || (param.wParam && !m_bCanHideColumn))
			{
				HDITEM hdi;				
				hdi.mask = HDI_LPARAM;
				int i,nCnt = GetCount();
				LPLONG lpl;
				//状态转变
				if(m_bCanHideColumn)
				{
					//原允许,删除所有列头内部信息数据
					for(i=1;i<nCnt;i++)
					{
						if(CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,i,(LPARAM)&hdi))
						{
							if(hdi.lParam)
							{
								lpl = (LPLONG)hdi.lParam;
								hdi.lParam = *lpl;
								CallWindowProc(m_DefProc,m_hWnd,HDM_SETITEM,i,(LPARAM)&hdi);
								Del(lpl);
							}
						}
					}
				}
				else
				{
					//原禁止,创建所有列头内部信息数据
					for(i=1;i<nCnt;i++)
					{
						if(CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,i,(LPARAM)&hdi))
						{
							lpl = (LPLONG)New(sizeof(LONG)*2);
							if(lpl)
							{
								*lpl = hdi.lParam;
								hdi.lParam = (LONG)lpl;
								lpl[1] = GetItemWidth(i) | (i << 16);
								CallWindowProc(m_DefProc,m_hWnd,HDM_SETITEM,i,(LPARAM)&hdi);
							}
						}
					}
				}
				m_bCanHideColumn = param.wParam;
			}
			return 0;
		case HDM_SHOWCOLUMN:
			//置指定列头显示/隐藏标记,第一列不可隐藏
			if(param.wParam > 0)
			{
				if(!m_bCanHideColumn)
					SendMessage(m_hWnd,HDM_ENABLECOLUMNHIDE,TRUE,0);
				
				HDITEM hd;
				hd.mask = HDI_LPARAM;
				hd.lParam = 0;
				if(CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,param.wParam,(LPARAM)&hd))
				{
					LPLONG lpl = (LPLONG)hd.lParam+1;
					if(lpl)
					{
						if(m_CoolMenu)
							m_CoolMenu->CheckItem(param.wParam,HDCOL_ISHIDDEN(*lpl),BY_POSITION);

						res = LOWORD(*lpl);
						//获取序号
						hd.mask = HDI_ORDER;
						hd.iOrder = param.wParam;
						CallWindowProc(m_DefProc,m_hWnd,HDM_GETITEM,param.wParam,(LPARAM)&hd);

						//显示lParam为TRUE,但记录时隐藏为TRUE,因此需反一下
						if(param.lParam)
						{
							if(HDCOL_ISHIDDEN(*lpl))
							{
								//显示,移动到原位置
								int pre=m_nHideColCnt,cnt = GetCount();
								LPINT lpi = (LPINT)_alloca(cnt*sizeof(int));
								GetOrderArray(cnt,lpi);
								hd.iOrder = HDCOL_EXTRACTORDER(*lpl);
								for(int i=m_nHideColCnt;i<cnt;i++)
								{
									if(hd.iOrder > lpi[i])
										pre = i;
									else
									{
										i = pre;
										break;
									}
								}
								m_nHideColCnt --;
								//hd.iOrder = HDCOL_EXTRACTORDER(*lpl) + m_nHideColCnt;
								*lpl = MAKELONG(res,hd.iOrder);
								hd.iOrder = min(i,cnt-1);								
							}
						}
						else
						{
							if(!HDCOL_ISHIDDEN(*lpl))
							{
								//隐藏
								*lpl = res | HDCOL_HIDEMASK | (HDCOL_EXTRACTORDER(*lpl)<<16);//((max(0,hd.iOrder-m_nHideColCnt)<<16)&0x0FFF0000);
								//移到最前面
								hd.iOrder = 0;
								m_nHideColCnt++;
							}
						}
						CallWindowProc(m_DefProc,m_hWnd,HDM_SETITEM,param.wParam,(LPARAM)&hd);
						return res;
					}
				}
			}
			return 0;
		case WM_LBUTTONDOWN:
		case WM_LBUTTONDBLCLK:
			if(IsMouseOnHiddenCol(param.mousept.x,param.mousept.y))
				return 0;
			break;
		case WM_RBUTTONDOWN:
			if(m_bCanHideColumn && m_CoolMenu)
				m_CoolMenu->Popup(m_hWnd);
			break;
		case WM_COMMAND:
			if(m_bCanHideColumn && m_CoolMenu && param.shortval.high == 0)
			{
				//菜单消息
				NMHEADER nmhd;
				int nIndex = param.shortval.low;
				BOOL bShow = (m_CoolMenu->GetItemCheckState(nIndex) == MF_UNCHECKED);
				HWND hWndP;
				if(Parent()->GetWindowType() == ITIS_LISTVIEW)
				{
					((RingListView*)Parent())->ShowColumn(nIndex - HDMENU_BASEID,bShow);
					nmhd.hdr.code = LVN_SHOWCOLUMN;
					nmhd.hdr.hwndFrom = Parent()->Handle();
					nmhd.hdr.idFrom = ((RingListView*)Parent())->GetId();
					hWndP = Parent()->GetParent();
				}
				else
				{
					ShowColumn(nIndex - HDMENU_BASEID,bShow);
					nmhd.hdr.code = HDN_SHOWCOLUMN;
					nmhd.hdr.hwndFrom = m_hWnd;
					nmhd.hdr.idFrom = GetId();
					hWndP = GetParent();
				}
				nmhd.iItem = nIndex - HDMENU_BASEID;
				nmhd.iButton = bShow;
				nmhd.pitem = NULL;
				SendMessage(hWndP,WM_NOTIFY,nmhd.hdr.idFrom,(LPARAM)&nmhd);
				return 0;
			}
			break;
		case WM_MEASUREITEM:      	
			if(m_CoolMenu)
				m_CoolMenu->MeasureItem(param.lpmeasureitem);
         break;
      case WM_DRAWITEM:
			if(m_CoolMenu)
				m_CoolMenu->Draw(param.lpdrawitem);
         break;
		case WM_DESTROY:
			SendMessage(m_hWnd,HDM_ENABLECOLUMNHIDE,FALSE,0);
			break;
	}
	return RingControls::DefaultProc(param);
}

BOOL RingHeader::IsMouseOnHiddenCol(int x,int y)
{
	if(m_bCanHideColumn)
	{
		HDHITTESTINFO hdii;
		hdii.pt.x = x;
		hdii.pt.y = y;
		LRESULT res = CallWindowProc(m_DefProc,m_hWnd,HDM_HITTEST,0,(LPARAM)&hdii);
		if(res != -1 && MASK_MATCH(hdii.flags,HHT_ONDIVOPEN))
		{
			LPLONG lpl = GetHDInfo(res);
			if(lpl && HDCOL_ISHIDDEN(*lpl))
				return TRUE;
		}
	}
	return FALSE;
}

//显示/隐藏列头,iCol为全部列头显示状态下的序号,显示/隐藏并不立即生效,
//必须随后设置宽度才可以,这样做是因为ListView设置列宽会同时设置本HEADER的列宽,
//函数返回原显示状态下列宽
int RingHeader::ShowColumn(int iCol,BOOL bShow)
{
	int nWidth = SendMessage(m_hWnd,HDM_SHOWCOLUMN,iCol,bShow);
	if(Parent()->GetWindowType() != ITIS_LISTVIEW)
	{
		if(bShow)
			SetItemWidth(iCol,nWidth);
		else
			SetItemWidth(iCol,0);
		InvalidateRect(m_hWnd,NULL,TRUE);
	}
	return nWidth;
}

//允许右键菜单选择显示/隐藏列头
RingCoolMenu* RingHeader::EnableColumnMenu(BOOL bEnable)
{
	if(bEnable)
	{
		EnableHideColumn();

		if(m_CoolMenu == NULL)
		{
			m_CoolMenu = new RingCoolMenu;
			if(m_CoolMenu)
			{
				if(m_CoolMenu->CreatePopup())
				{
					int nCnt = GetCount();
					if(nCnt > 0)
					{
						m_CoolMenu->Insert(HDMENU_BASEID,0,(LPSTR)GetText(0),BY_POSITION);
						m_CoolMenu->EnableItem(0,FALSE,BY_POSITION);
						m_CoolMenu->CheckItem(0,TRUE,BY_POSITION);

						for(int i=1;i<nCnt;i++)
						{
							m_CoolMenu->Insert(HDMENU_BASEID+i,i,(LPSTR)GetText(i),BY_POSITION);
							m_CoolMenu->CheckItem(i,!IsColumnHide(i),BY_POSITION);
						}
					}
				}
			}
		}
	}
	else
	{
		delete m_CoolMenu;
		m_CoolMenu = NULL;
	}
	return m_CoolMenu;
}